#include <stdio.h>
#include <string.h>
#include "w25qxx.h"
#include "led.h"
#include "usart1.h"

/* 初始化SPI要用到的片选口 */
const IOList w25qxx_gpio_def[5] = {{GPIOA, {GPIO_Pin_3, GPIO_Speed_50MHz, GPIO_Mode_Out_PP}, 1}, {0, {0, 0, 0}, 0}};

// 服务函数
uint16_t flash_get_id(void) // 读取ID
{
    uint16_t id = 0;
    // 发送读制造/器件号指令 0x90
    Flash_Chip_Select;
    flash_byte_write(0x90);
    // 发送24位地址
    flash_byte_write(0x00);
    flash_byte_write(0x00);
    flash_byte_write(0x00);
    id = flash_byte_read() << 8; // 生产ID
    id |= flash_byte_read();     // 器件ID
    Flash_Chip_Deselect;
    return id;
}
void flash_write_enable(void) // 写使能
{
    Flash_Chip_Select;
    flash_byte_write(0x06); // 写使能
    Flash_Chip_Deselect;
}
uint8_t flash_read_state(void) // 读取状态寄存器
{
    uint8_t state = 0;
    Flash_Chip_Select;
    flash_byte_write(0x05);
    state = flash_byte_read(); // 读取状态值
    Flash_Chip_Deselect;
    return state;
}
void flash_erace_chip() // 擦除芯片
{
    while (flash_read_state() & W25Qxx_BUSY) // 等待忙结束
    {
    }
    flash_write_enable(); // 写使能
    Flash_Chip_Select;
    flash_byte_write(0xC7); // 芯片擦除
    Flash_Chip_Deselect;
}
void _flash_sector_erace(uint32_t addr) // 扇区擦除
{
    while (flash_read_state() & W25Qxx_BUSY) // 等待忙结束
    {
    }
    flash_write_enable(); // 写使能
    Flash_Chip_Select;
    flash_byte_write(0x20);                  // 扇区擦除
    flash_byte_write((uint8_t)(addr >> 16)); // 发送24位地址
    flash_byte_write((uint8_t)(addr >> 8));
    flash_byte_write((uint8_t)(addr));
    Flash_Chip_Deselect;
}
void _flash_page_write(uint32_t addr, uint8_t *data, uint16_t size) // 单编程
{
    uint8_t *pData = data;
    if (addr + size - 1 > W25Qxx_MAX_ADDR)
        return;
    while (flash_read_state() & W25Qxx_BUSY) // 等待忙结束
    {
    }
    flash_write_enable(); // 写使能
    while (!(flash_read_state() & W25Qxx_WEL))
    {
    }
    Flash_Chip_Select;
    flash_byte_write(0x02);       // 页写指令
    flash_byte_write(addr >> 16); // 发送24位地址
    flash_byte_write(addr >> 8);
    flash_byte_write(addr);
    while (size--)
    {
        flash_byte_write(*pData);
        pData++;
    }
    Flash_Chip_Deselect;
}
void _flash_stepover_pagewrite(uint32_t addr, uint8_t *data, uint32_t size) // 多页编程
{
    uint32_t addr_remain = 256 - addr % 256; // 参数给的addr之后,当前页地址剩余多少字节
    uint8_t *pData = data;
    if (size <= addr_remain) // 当前页就够了
    {
        addr_remain = size;
    }
    while (1)
    {
        _flash_page_write(addr, pData, addr_remain);
        if (addr_remain == size)
        {
            break; // 数据全部写入
        }
        pData += addr_remain;    // buf数据地址偏移
        addr += addr_remain;     // flash地址偏移
        size -= addr_remain;     // size计算剩余数据
        addr_remain = 256;       // 写入一页数据
        if (size <= addr_remain) // 计算当前页是否够写入剩余数据
        {
            addr_remain = size;
        }
    }
}

// 业务函数
void flash_write_data(uint32_t addr, uint8_t *data, uint32_t size) // 写
{
    uint8_t sector_data[4096];
    uint16_t sector_offset = addr % 4096;          // 地址位于扇区的哪一个位置
    uint16_t sector_remain = 4096 - sector_offset; // 地址所在扇区剩余空间
    uint32_t sector_addr = addr - sector_offset;   // 计算当前扇区的起始地址
    uint8_t *pData = data;
    uint32_t i;
    if (size <= sector_remain)
    {
        sector_remain = (uint16_t)size; // 单个扇区多少字节要写
    }
    while (1)
    {
        flash_read_data(addr, sector_data, sector_remain); // 读取要写入地址的数据,看看是不是为全0xFF
        for (i = 0; i < sector_remain; i++)
        {
            if (sector_data[i] != 0xFF)
                break;
        }
        if (i != sector_remain) // 如果不是就要擦除扇区,判断是否需要擦除扇区
        {
            // 擦除前保存当前扇区前一段数据
            flash_read_data(sector_addr, sector_data, sector_offset);
            // 擦除前保存当前扇区后一段数据
            flash_read_data(addr + sector_remain,
                            sector_data + (sector_offset + sector_remain),
                            W25Qxx_SECTOR_SIZE - (sector_offset + sector_remain));
            _flash_sector_erace(sector_addr); // 擦除扇区
            // 将要写入的数据插入缓冲区
            for (i = 0; i < sector_remain; i++)
            {
                sector_data[sector_offset + i] = pData[i];
            }
            _flash_stepover_pagewrite(sector_addr, sector_data, W25Qxx_SECTOR_SIZE);
            sector_offset = 0;
        }
        else
        {
            _flash_stepover_pagewrite(addr, pData, sector_remain); // 向当前扇区写入数据
        }
        if (sector_remain == size)
            break; // 全部数据完全写入

        pData += sector_remain;             // 数据地址偏移
        addr += sector_remain;              // flash地址偏移
        sector_addr = addr;                 // 当前扇区起始地址
        size -= sector_remain;              // 数据量减少
        sector_remain = W25Qxx_SECTOR_SIZE; // 当前扇区剩余
        if (size <= W25Qxx_SECTOR_SIZE)     // 计算当前扇区是否够写入剩余数据
        {
            sector_remain = size;
        }
    }
}
void flash_read_data(uint32_t addr, uint8_t *data, uint16_t size) // 读
{
    uint8_t *pData = data;
    while (flash_read_state() & W25Qxx_BUSY) // 等待忙结束
    {
    }
    Flash_Chip_Select;
    flash_byte_write(0x03);       // 读数据指令
    flash_byte_write(addr >> 16); // 发送24位地址
    flash_byte_write(addr >> 8);
    flash_byte_write(addr);
    while (size--)
    {
        *pData = flash_byte_read(); // 保存数据
        pData++;
    }
    Flash_Chip_Deselect;
}
void flash_read_data_via_dma(uint32_t addr, uint8_t *data, uint16_t size)
{
    uint8_t *p = data + 1;
    // 等待忙结束
    while (flash_read_state() & W25Qxx_BUSY)
    {
    }
    Flash_Chip_Select;

    // 准备要发送到w25qxx的数据
    *p++ = 0x0B;
    *p++ = addr >> 16;
    *p++ = addr >> 8;
    *p++ = addr;

    // 设置DMA地址和长度
    DMA1_Channel2->CMAR = (uint32_t)data;
    DMA1_Channel3->CMAR = (uint32_t)data + 1;
    DMA1_Channel2->CNDTR = size + 6;
    DMA1_Channel3->CNDTR = size + 6;

    DMA1_Channel2->CCR |= DMA_CCR1_EN;
    DMA1_Channel3->CCR |= DMA_CCR1_EN;
}

uint8_t flash_wait_reflash_cmd(uint16_t timeout)
{
    uint32_t count = 0;
    uint32_t cmd = 0;
    IO_RS485_R0T1 = 1;
    while (1)
    {
        add_1_per_1ms(&count);
        if (count >= timeout)
        {
            IO_RS485_R0T1 = 0;
            return 0;
        }
        if (USART1->SR & USART_FLAG_RXNE)
        {
            cmd >>= 8;
            *(uint8_t *)((uint32_t)&cmd + 3) = USART1->DR;
            if (cmd == YES_RE_FLASH)
            {
                return 1;
            }
        }
    }
}
void flash_w25qxx(uint32_t baud)
{
    volatile uint32_t addr, size, cmd, count;
    uint8_t received_data[4096];
    // 设置编程flash波特率
    usart1_init_without_IRQ(baud);
    // 查看是否需要写flash
    if (flash_wait_reflash_cmd(500) == 0)
    {
        return;
    }
    // display_static_text((uint8_t*)"Flashing");
    led_turn_off();
    IO_RS485_R0T1 = 1;
    // 循环索取命令和数据
    while (1)
    {
        cmd = 0;
        Transmit_1byte(START_RECEIVE_TASK);
        usart1_read_data((uint8_t *)&cmd, 4, 500);
        switch (cmd)
        {
        case GET_FLASH_ID:
            Transmit_1byte(START_SEND_DATA);
            Transmit_1byte(flash_get_id() >> 8);
            Transmit_1byte(flash_get_id());
            Transmit_1byte(TASK_FINISH);
            break;

        case PROGRAM:
            // 读地址
            Transmit_1byte(READ_PRAMA);
            usart1_read_data((uint8_t *)&addr, 4, 20);
            // 读个数
            Transmit_1byte(READ_PRAMA);
            usart1_read_data((uint8_t *)&size, 4, 20);
            // 读数据
            Transmit_1byte(READ_PRAMA);
            count = usart1_read_data(received_data, size, 700);
            // 写入flash
            if (count == size)
            {
                flash_write_data(addr, received_data, size);
                // 反馈任务完成
                Transmit_1byte(TASK_FINISH);
            }
            break;

        case READDATA:
            // 读地址
            Transmit_1byte(READ_PRAMA);
            usart1_read_data((uint8_t *)&addr, 4, 20);
            // 读个数
            Transmit_1byte(READ_PRAMA);
            usart1_read_data((uint8_t *)&size, 4, 20);
            // 反馈
            if (addr + size > W25Qxx_MAX_ADDR + 1)
            {
                Transmit_1byte(ERROR);
                break;
            }
            Transmit_1byte(START_SEND_DATA);
            // 等待忙结束
            while (flash_read_state() & W25Qxx_BUSY)
            {
            }
            Flash_Chip_Select;
            flash_byte_write(0x03);       // 读数据指令
            flash_byte_write(addr >> 16); // 发送24位地址
            flash_byte_write(addr >> 8);
            flash_byte_write(addr);
            while (size--)
            {
                Transmit_1byte(flash_byte_read());
            }
            Flash_Chip_Deselect;
            break;

        case PROGRAM_EXIT:
            Transmit_1byte(TASK_FINISH);
            // 恢复485发送接收选择IO口
            IO_RS485_R0T1 = 0;
            led_turn_on();
            display_text_left_shift((uint8_t *)"Programing Finished!");
            wait_for_left_shift_count(1);
            return;

            // case WRITE_ZK_OK_FLAG:
            //     flash_write_data(ADDR_ZK_OK, (uint8_t *)zk_ok_flag, 2);
            //     Transmit_1byte(zk_is_set_ok() ? TASK_FINISH : ERROR);
            //     break;

            case ERASE_CHIP:
                flash_erace_chip();
                while (flash_read_state() & W25Qxx_BUSY) // 等待忙结束
                {
                    Transmit_1byte(TASK_BUSY);
                    Delay_ms(100);
                }
                Transmit_1byte(TASK_FINISH);
                break;
        }
    }
}

// 初始化函数
void spi1_dma_init(void)
{
    DMA_InitTypeDef DMA_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    /* DMA1 Channel2 (triggered by SPI1 Rx event) Config */
    DMA_DeInit(DMA1_Channel2);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = 0;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 0;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel2, &DMA_InitStructure);

    /* Enable SPI1 DMA RX request */
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);
    DMA_Cmd(DMA1_Channel2, DISABLE);

    /* DMA1 Channel3 (triggered by SPI1 Tx event) Config */
    DMA_DeInit(DMA1_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = 0;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 0;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel3, &DMA_InitStructure);

    /* Enable SPI1 DMA TX request */
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
    DMA_Cmd(DMA1_Channel3, DISABLE);

    /* DMA中断设置 */
    NVIC_SetPriority(DMA1_Channel3_IRQn, 6);
    NVIC_EnableIRQ(DMA1_Channel3_IRQn);

    DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);
}
void w25qxx_init()
{
    gpio_init((IOList *)w25qxx_gpio_def);
    spi1_init();
    spi1_dma_init();
    flash_write_enable();
}
void DMA1_Channel3_IRQHandler()
{
    DMA1_Channel2->CCR &= (uint16_t)(~DMA_CCR1_EN);
    DMA1_Channel3->CCR &= (uint16_t)(~DMA_CCR1_EN);
    DMA1->IFCR = DMA1_FLAG_TC3 | DMA1_FLAG_TC2;
    Flash_Chip_Deselect;
}